package com.wx.task;

import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 自定义缓存类
 * 用hashMap 进行存储
 * 用读写锁保证线程安全
 *
 * @author wang.xun
 * @date 2023/07/31
 */
@Slf4j
public class MyCache<K, V> {
    final Map<K, V> data = new HashMap<>();
    /**
     * 可重入读写锁
     */
    final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    /**
     * 读锁
     */
    final Lock readLock = lock.readLock();
    /**
     * 写锁
     */
    final Lock writeLock = lock.writeLock();


    public V get(K k) {
        readLock.lock();
        try {
            V v = data.get(k);
            return v == null ? null : v;
        } finally {
            readLock.unlock();
        }
    }

    public void set(K k, V v) {
        writeLock.lock();

        try {
            data.put(k, v);
        } finally {
            writeLock.unlock();
        }
    }


    /**
     * 懒加载
     *
     * @param k k
     * @return {@link V}
     */
    public V lazyGet(K k) {
        readLock.lock();
        try {
            V v = data.get(k);
            if(v != null){
                return v;
            }else{
                // 读写锁不能升级，要先释放读锁，再获取写锁
                readLock.unlock();
            }

            writeLock.lock();
            try {
                // 重复判断，是否存在值，有可能获取到写锁的时候，已经有其他线程提前将值设置过了
                V v1 = data.get(k);
                if(v1 == null){
                    // TODO 读写数据库或查询其他数据源

                    data.put(k, (V)"default");
                    // 锁升级不可以，但是可以支持锁的降级，为了兼容最后finally 读锁释放
                    readLock.lock();
                }
                return data.get(k);
            } finally {
                writeLock.unlock();
            }
        } finally {
            readLock.unlock();
        }
    }


    public static void main(String[] args) {
        MyCache<String, String> myCache = new MyCache<>();
        myCache.set("1","zg");

        log.info("1 的值：{}",myCache.get("1"));

        log.info("2 的值：{}",myCache.get("2"));
        log.info("lazyGet 2 的值：{}",myCache.lazyGet("2"));
    }

}
